﻿#include "RePackingMp4Window.h"
#include <QFileDialog>
#include <QSize>
#include <QMessageBox>
#include <Qt>
#include <QDebug>

extern "C"
{
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libavutil/avutil.h"
}
//#include "../codec/VideoReScaler.h"

RePackingMp4Window::RePackingMp4Window(QWidget* parent)
	: QWidget(parent)
{
	ui.setupUi(this);
	this->setWindowTitle("重新封装MP4，如果是网络流则变为录制流到本地");
	setAttribute(Qt::WA_DeleteOnClose, true);  // 关闭时自动销毁窗口
}

void RePackingMp4Window::OnSrcFileSelected()
{
	QString filter = "所有文件(*.*)"; //文件过滤器
	QString filePath = QFileDialog::getOpenFileName(this, "选择输入文件", "", filter);
	this->srcFilePath = filePath;
	this->ui.lineEdit_src_path->setText(this->srcFilePath);
}

void RePackingMp4Window::OnDstOutputFileSelected()
{
	QString filter = "所有文件(*.*)"; //文件过滤器
	QString filePath = QFileDialog::getSaveFileName(this, "选择输出文件", "", filter);
	this->dstFilePath = filePath;
	this->ui.lineEdit_target_path->setText(this->dstFilePath);
}

void RePackingMp4Window::OnStartTransfer()
{
	if (this->dstFilePath.isEmpty() || this->srcFilePath.isEmpty()) {
		QMessageBox::information(this, "温馨提示", "参数不对请检查参数");
		return;
	}
	begin_seconds = this->ui.lineEdit_start_time->text().toDouble();//开始截取时间
	end_seconds = this->ui.lineEdit_end_time->text().toDouble();//结束截取时间
	isRunning = true;
	renderThread = std::make_unique<std::thread>(&RePackingMp4Window::OnTransfer, this);
}

void RePackingMp4Window::PrintError(int err, QString msg)
{
	char buf[1024] = { 0 };
	av_strerror(err, buf, sizeof(buf) - 1);
	qDebug() << msg << "->" << buf;
}
/// <summary>
/// 开始转换
/// </summary>
void RePackingMp4Window::OnTransfer()
{


	//1.创建解封装上下文，并打开文件
	AVFormatContext* ic = nullptr;
	int re = avformat_open_input(&ic, this->srcFilePath.toStdString().c_str(), NULL, NULL);
	if (re != 0)
	{
		PrintError(re, "avformat_open_input");
		return;
	}
	//2.发现媒体信息流
	re = avformat_find_stream_info(ic, NULL);
	if (re < 0)
	{
		PrintError(re, "avformat_find_stream_info");
		return;
	}
	//3.分别找到音频流以及视频流
	AVStream* vs = nullptr;
	AVStream* as = nullptr;
	for (int i = 0;i < ic->nb_streams;i++)
	{
		if (ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			as = ic->streams[i];
		}
		else if (ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			vs = ic->streams[i];
		}
	}

	//VideoReScaler* scaler = new VideoReScaler();
	//scaler->Init(vs->codecpar->width, vs->codecpar->height, outWidth, outHeight);


	///4.创建封装器上下文
	AVFormatContext* oc = nullptr;
	re = avformat_alloc_output_context2(&oc, NULL, NULL, this->dstFilePath.toStdString().c_str());
	if (re < 0)
	{
		PrintError(re, "avformat_alloc_output_context2");
		return;
	}
	//添加音频流以及视频流
	auto ovs = avformat_new_stream(oc, NULL);
	auto oas = avformat_new_stream(oc, NULL);
	//将输入流的编解码参数，复制到输出流中
	if (vs)
	{
		ovs->time_base = vs->time_base;
		avcodec_parameters_copy(ovs->codecpar, vs->codecpar);
	}
	if (as)
	{
		oas->time_base = as->time_base;
		avcodec_parameters_copy(oas->codecpar, as->codecpar);
	}

	if (begin_seconds > 0 && end_seconds > 0)
	{
		//计算开始的pts和结束的pts
	   //pts = seconds/(num/den)=seconds*(den/num)
		if (vs && vs->time_base.num > 0)
		{
			double t = (double)vs->time_base.den / (double)vs->time_base.num;//den分母/num分子
			begin_pts = begin_seconds * t;
			end_pts = end_seconds * t;
		}
		if (as && as->time_base.num > 0)
		{
			begin_audio_pts = begin_seconds * ((double)as->time_base.den / (double)as->time_base.num);
		}
		//移动到关键帧seek
		if (vs)
		{
			re = av_seek_frame(ic, vs->index, begin_pts, AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD);
			PrintError(re, "av_seek_frame");
		}
	}


	//打开输出流
	re = avio_open(&oc->pb, this->dstFilePath.toStdString().c_str(), AVIO_FLAG_WRITE);
	if (re < 0)
	{
		PrintError(re, "avio_open");
		return;
	}
	//写入文件头
	re = avformat_write_header(oc, NULL);
	if (re < 0)
	{
		PrintError(re, "avformat_write_header");
		return;
	}
	AVPacket pkt;
	while (isRunning)
	{
		//解封装
		re = av_read_frame(ic, &pkt);
		if (re != 0)
		{
			PrintError(re, "av_read_frame");
			break;
		}
		if (begin_seconds > 0 && end_seconds > 0)
		{
			AVStream* in_stream = ic->streams[pkt.stream_index];
			AVStream* out_stream = nullptr;
			long long offset_pts = 0; //偏移pts，用于截断的开头pts运算
			if (vs && pkt.stream_index == vs->index)
			{
				qDebug() << "视频:";
				//超过设定的时间就退出
				if (pkt.pts > end_pts)
				{
					av_packet_unref(&pkt);
					break;
				}
				out_stream = oc->streams[0];
				offset_pts = begin_pts;
			}
			else if (as && pkt.stream_index == as->index)
			{
				qDebug() << "音频:";
				out_stream = oc->streams[1];
				offset_pts = begin_audio_pts;
			}
			//重新计算pts dts duration
			//`a * bq（输入basetime） / cq（输出basetime）`
			if (out_stream)
			{
				pkt.pts = av_rescale_q_rnd(pkt.pts - offset_pts, in_stream->time_base,
					out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)
				);
				pkt.dts = av_rescale_q_rnd(pkt.dts - offset_pts, in_stream->time_base,
					out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)
				);
				pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
			}
			pkt.pos = -1;
		}

		//写入音视频帧，此方法会自动清理AVPacket
		re = av_interleaved_write_frame(oc, &pkt);
		if (re != 0)
		{
			PrintError(re, "av_interleaved_write_frame");
		}
	}
	//写入尾部
	re = av_write_trailer(oc);
	if (re != 0)
	{
		PrintError(re, "av_write_trailer");
	}
	//关闭解封装输入上下文
	avformat_close_input(&ic);

	//关闭写入IO
	avio_closep(&oc->pb);
	//释放申请的输出解封装上下文
	avformat_free_context(oc);
	oc = nullptr;
	/*if (scaler)
	{
		scaler->Release();
		scaler = nullptr;
	}*/

	qDebug() << "解封装结束";


}

void RePackingMp4Window::closeEvent(QCloseEvent* event) {
	event->accept();  // 接受关闭事件（允许关闭）
	if (isRunning && renderThread->joinable()) {
		renderThread->join();
		renderThread = nullptr;
	}
	qDebug() << "窗口已关闭";
}

RePackingMp4Window::~RePackingMp4Window()
{
}
